"""
认证工具模块 - 处理JWT token的创建和验证

JWT (JSON Web Token) 是一种开放标准(RFC 7519)，用于在各方之间安全地传输信息。
它由三部分组成：头部(Header)、载荷(Payload)和签名(Signature)。

在我们的项目中，JWT用于用户身份验证，工作流程如下：
1. 用户登录成功后，服务器生成JWT token并返回给客户端
2. 客户端在后续请求中将token放在Authorization头中发送给服务器
3. 服务器验证token的有效性，确认用户身份
4. 服务器根据token中的信息识别用户并提供相应服务

JWT的优势：
1. 无状态：服务器不需要存储会话信息
2. 跨域支持：token可以轻松在不同域之间传递
3. 移动端友好：适合移动应用的身份验证
4. 自包含：token中包含了用户信息，减少数据库查询
"""

# 从datetime模块导入datetime和timedelta类，用于处理时间
# datetime: 处理日期和时间
# timedelta: 表示两个日期或时间之间的差值
from datetime import datetime, timedelta

# 从typing模块导入Optional，用于类型提示
# Optional[T] 等价于 Union[T, None]，表示参数可以是T类型或None
from typing import Optional

# 从jose库导入JWTError异常和jwt模块，用于JWT操作
# JWTError: JWT相关操作出现错误时抛出的异常
# jwt: 提供JWT编码和解码功能的模块
from jose import JWTError, jwt

# 从config模块导入settings，包含应用配置
# settings包含应用的各种配置项，如密钥、算法、过期时间等
from config import settings


def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    """
    创建JWT访问令牌 - 用户登录成功后调用此函数生成token
    
    JWT token结构：
    1. Header(头部)：包含算法和令牌类型
    2. Payload(载荷)：包含用户信息和过期时间等声明
    3. Signature(签名)：用于验证令牌未被篡改
    
    参数:
        data: 要编码到token中的数据字典，通常包含用户标识等信息
              例如: {"sub": "1234567890", "name": "John Doe", "admin": true}
        expires_delta: token过期时间增量，可选参数
                      如果不提供，使用settings.ACCESS_TOKEN_EXPIRE_HOURS作为默认值
    
    返回:
        编码后的JWT字符串，格式为: header.payload.signature
    
    实际应用示例：
    当用户使用钉钉登录成功后，我们会调用此函数：
    token_data = {"sub": str(user.id), "dingtalk_id": openid}
    jwt_token = create_access_token(token_data)
    """
    # 复制数据字典，避免修改原始数据
    # 这是一个良好的编程习惯，防止副作用
    to_encode = data.copy()
    
    # 计算过期时间
    if expires_delta:
        # 如果提供了过期时间增量，使用它计算过期时间
        # datetime.utcnow()获取当前UTC时间
        # timedelta表示时间差，expires_delta是时间增量
        expire = datetime.utcnow() + expires_delta
    else:
        # 如果没有提供过期时间增量，使用默认配置计算过期时间
        # settings.ACCESS_TOKEN_EXPIRE_HOURS是配置的过期小时数
        expire = datetime.utcnow() + timedelta(hours=settings.ACCESS_TOKEN_EXPIRE_HOURS)

    # 将过期时间添加到要编码的数据中
    # "exp"是JWT标准中表示过期时间的声明字段
    # expire是datetime对象，会被自动转换为Unix时间戳
    to_encode.update({"exp": expire})
    
    # 使用JWT库编码数据，生成token
    # jwt.encode()函数执行以下操作：
    # 1. 将头部、载荷进行Base64Url编码
    # 2. 使用指定的算法和密钥对头部+载荷进行签名
    # 3. 将三部分用"."连接起来形成最终的JWT token
    encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
    
    # 返回生成的JWT token
    # 客户端需要在后续请求的Authorization头中携带此token
    # 格式为: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    return encoded_jwt


def verify_token(token: str):
    """
    验证JWT token的有效性 - 在需要认证的接口中调用此函数验证用户身份
    
    验证过程：
    1. 检查token格式是否正确
    2. 验证签名是否有效（确保token未被篡改）
    3. 检查token是否已过期
    4. 检查其他声明（如发行者、受众等，如果有的话）
    
    参数:
        token: 要验证的JWT字符串
               格式为: header.payload.signature
    
    返回:
        如果token有效，返回解码后的数据字典（包含用户信息和过期时间等）
        如果token无效（过期、签名错误等），返回None
    
    实际应用示例：
    在需要认证的接口中：
    credentials = Depends(security)  # 从请求头中提取token
    token = credentials.credentials
    payload = verify_token(token)    # 验证token并获取用户信息
    if not payload:
        # token无效，抛出401错误
        raise HTTPException(status_code=401, detail="无效的token")
    """
    try:
        # 尝试解码JWT token
        # jwt.decode()函数执行以下操作：
        # 1. 分割token为三部分（header, payload, signature）
        # 2. 验证签名是否正确（使用配置的密钥和算法）
        # 3. 检查过期时间（exp声明）
        # 4. 返回解码后的载荷数据
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
        
        # 如果解码成功，返回payload数据
        # payload包含我们在create_access_token中编码的所有数据
        # 例如: {"sub": "123", "dingtalk_id": "abc", "exp": 1234567890}
        return payload
    except JWTError:
        # 如果解码失败（如过期、签名错误等），返回None
        # JWTError可能由以下情况引发：
        # 1. token格式错误
        # 2. 签名验证失败（token被篡改）
        # 3. token已过期
        # 4. token尚未生效（如果有nbf声明）
        return None